package inorder_to_postorder.lexer;

import inorder_to_postorder.symbols.Type;

import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;

public class Lexer {
    public int line = 1;
    char peek = ' ';
    HashMap<String, Word> words = new HashMap<>();
    Reader reader = null;

    void reserve(Word w) {
        words.put(w.lexeme, w);
    }

    public Lexer(Reader r) {
        reserve(Word.IF);
        reserve(Word.ELSE);
        reserve(Word.TRUE);
        reserve(Word.FALSE);
        reserve(Word.EQ);
        reserve(Word.RIPEMD160);
        reserve(Word.SHA1);
        reserve(Word.SHA256);
        reserve(Word.HASH160);
        reserve(Word.HASH256);
        reserve(Word.VERIFY);
        reserve(Word.DROP);
        reserve(Word.DUP);
        reserve(Word.CHECKLOCKTIMEVERIFY);
        reserve(Word.CHECKSEQUENCEVERIFY);
        reserve(Word.CHECKSIG);
        reserve(Word.CHECKSIGVERIFY);
        reserve(Word.EQUALVERIFY);
        reserve(Word.CHECKMULTISIG);
        reserve(Word.CHECKMULTISIGVERIFY);
        reserve(Word.TEMP);
        reserve(Word.EQUAL);
        reserve(Type.INT);
        reserve(Type.FLOAT);
        reserve(Type.STR);

        reader = r;
    }

    void readch() throws IOException {
        peek = (char) reader.read();
    }

    boolean readch(char c) throws IOException {
        readch();
        if (peek != c) return false;
        peek = ' ';
        return true;
    }

    void error(String s) {
        String error_msg = "在第" + line + "行：" + s;
        byte[] bytes = error_msg.getBytes(StandardCharsets.UTF_8);
        String value = new String(bytes, StandardCharsets.UTF_8);
        throw new Error(value);
    }

    public Token scan() throws IOException {
        for (; ; readch()) {
            if (peek == ' ' || peek == '\t' || peek == '\r') continue;
            else if (peek == '\n') line = line + 1;
            else break;
        }

        // 判断是否是等于
        switch (peek) {
            case '=':
                if (readch('=')) return Word.EQ;
                else return new Token('=');
        }

        // 读取数字
        if (Character.isDigit(peek)) {
            int v = 0;
            do {
                v = 10 * v + Character.digit(peek, 10);
                readch();
            } while (Character.isDigit(peek));
            return new Num(v);
        }

        // 读取字符串或关键字
        if (Character.isLetter(peek)) {
            StringBuffer b = new StringBuffer();
            do {
                b.append(peek);
                readch();
            } while (Character.isLetterOrDigit(peek) || peek == '_');
            String s = b.toString();
            Word w = (Word) words.get(s);
            if (w != null) return w;
            w = new Word(s, Tag.ID);
            words.put(s, w);
            return w;
        }

        // 遇到双引号，读取字符串
        if (peek == '"') {
            StringBuffer b = new StringBuffer();
            readch();
            while (peek != '"') {
                if (peek == '\n') {
                    error("语法错误，缺少：\"");
                }
                b.append(peek);
                readch();
            }
            peek = ' ';
            return new Word(b.toString(), Tag.STR);
        }

        Token tok = new Token(peek);
        peek = ' ';
        return tok;
    }
}
